import tkinter as tk
from gui import Gui
from ipaddress import IPv4Interface, IPv4Network


class LanCalc(Gui):
    def __init__(self):
        super(LanCalc, self).__init__()
        self.button['command'] = self.main_func
        self.sub_prefix.config(command=self.sub_prefix_func)
        self.sub_count.bind('<<ComboboxSelected>>', self.sub_count_func)
        self.sub_len.bind('<<ComboboxSelected>>', self.sub_len_func)
        self.subnets_tree.bind('<Double-Button-1>', self.treeview_copy)
        self.bind('<Key>', self.press_key)

    def press_key(self, event):
        """
        Функция для захвата нажатий по клавиатуре
        """
#        print(event)
        if event.keysym in ('Return', 'KP_Enter', 'space'):            # Запуск расчета
            self.main_func()
        if event.keysym == 'Up':                # Уменьшить маску, и посчитать
            prefix_cur = self.entr_prefix.current()
            if 0 < prefix_cur:
                self.prefix_change(prefix_cur - 1)
                self.main_func()
        if event.keysym == 'Down':              # Увеличить маску, и посчитать
            prefix_cur = self.entr_prefix.current()
            if prefix_cur < 32:
                self.prefix_change(prefix_cur + 1)
                self.main_func()
        if event.keysym == 'Escape':
            self.entr_ipaddr.focus_set()
            self.entr_ipaddr.delete(0, tk.END)

    def treeview_copy(self, event):
        item = self.subnets_tree.item(self.subnets_tree.focus())
        col = self.subnets_tree.identify_column(event.x)
        col = int(col.replace('#', ''))
        item = item['values'][col-1]
        self.clipboard_clear()
        self.clipboard_append(item)

    def main_func(self):
        """
        Функция получения введенных данных и вычесления параметров
        """
        try:
            ipaddr, prefix = self.data_raw()
        except TypeError:
            self.entry_error()
        self.ipaddr = ipaddr
        self.prefix = prefix
        data = self.calc(self.ipaddr, self.prefix)
        if data:
            self.entry_val(data)

        # После получения введенных основных данных,
        # обновляем исходя из них пределы для расчета подсетей (вкладки Subnets)
        self.sub_prefix.config(from_=self.prefix)
        self.sub_prefix_change(self.prefix+1)

        self.sub_count_val = [str(2**i) for i in range(0, self.sub_count_list.index(self.prefix) + 1)]
        self.sub_count['values'] = self.sub_count_val
        if self.prefix != 32:
            calc_prefix = self.prefix + 1
        else:
            calc_prefix = self.prefix
        idx = calc_prefix - self.prefix
        self.sub_count_change(idx)

        len_vals = 32 - self.prefix
        self.sub_len_val = list(
                          reversed(
                                   [str(1) if i == 0
                                    else str(2) if i == 1
                                    else str(2**i - 2)
                                    for i in range(0, len_vals + 1)]
                                   )
                           )
        self.sub_len['values'] = self.sub_len_val
        self.sub_len_change(idx)

        # Ну и сразу вычесляем подсети исходя из
        # резделения первоначальной сети пополам
        self.sub_prefix_func()

    def sub_prefix_func(self):
        """
        Функция отображения вычесленных параметров subnets
        """
        calc_prefix = self.sub_prefix.get()
        subnets_data = self.calc_subnets(self.ipaddr, self.prefix, calc_prefix)
        if subnets_data:
            self.subnets_entry_val(subnets_data)
        idx = int(calc_prefix) - self.prefix
        self.sub_count_change(idx)
        self.sub_len_change(idx)

    def sub_count_func(self, event):
        count = self.sub_count.get()
        if count:
            idx = self.sub_count['values'].index(count)
            calc_prefix = self.prefix + idx
            subnets_data = self.calc_subnets(self.ipaddr, self.prefix, calc_prefix)
            self.sub_prefix_change(calc_prefix)
            self.sub_len_change(idx)
            if subnets_data:
                self.subnets_entry_val(subnets_data)

    def sub_len_func(self, event):
        len_val = self.sub_len.get()
        if len_val:
            idx = self.sub_len['values'].index(len_val)
            calc_prefix = self.prefix + idx
            subnets_data = self.calc_subnets(self.ipaddr, self.prefix, calc_prefix)
            self.sub_prefix_change(calc_prefix)
            self.sub_count_change(idx)
            if subnets_data:
                self.subnets_entry_val(subnets_data)

    def data_raw(self):
        ipaddr_raw = self.entr_ipaddr.get().replace(',', '.')
        if '/' in ipaddr_raw:
            try:
                ipaddr_raw, prefix_raw = ipaddr_raw.split('/')
                prefix_raw = int(prefix_raw)
            except ValueError:
                self.entry_error()
            try:
                if 0 <= prefix_raw <= 32:
                    self.prefix_change(prefix_raw)
            except (TypeError, ValueError):
                self.entry_error()
                return
            try:
                IPv4Interface(ipaddr_raw + '/' + str(prefix_raw))
            except (TypeError, ValueError):
                self.entry_error()
                return
        else:
            prefix_raw = self.entr_prefix.current()
        try:
            IPv4Interface(ipaddr_raw + '/' + str(prefix_raw))
        except (TypeError, ValueError):
            self.entry_error()
            return
        return ipaddr_raw, prefix_raw

    def calc(self, ipaddr, prefix):
        """
        Функция для вычесления параметров ip адреса
        """
        data = {'ipaddr': ipaddr, 'prefix': str(prefix)}
        interface = IPv4Interface(data['ipaddr'] + '/' + data['prefix'])
        try:
            data['ip_network'] = interface.network.network_address.compressed
            data['ip_broadcast'] = interface.network.broadcast_address.compressed
            data['netmask'] = interface.netmask.compressed
            data['wildcard'] = interface.hostmask.compressed
            len_network = int(interface.network.broadcast_address) - int(interface.network.network_address) - 1
            if prefix < 31:
                data['first_ip'] = (interface.network.network_address + 1).compressed
                data['last_ip'] = (interface.network.broadcast_address - 1).compressed
                data['len_network'] = str('{0:,}'.format(len_network).replace(',', ' '))
            elif prefix == 31:
                data['first_ip'] = data['ip_network']
                data['last_ip'] = data['ip_broadcast']
                data['len_network'] = str(2)
            elif prefix == 32:
                data['first_ip'] = data['ipaddr']
                data['last_ip'] = data['ipaddr']
                data['len_network'] = str(1)
        except UnboundLocalError:
            self.entry_error()
            return
        return data

    def calc_subnets(self, ipaddr, prefix, calc_prefix):
        """
        Функция отображения вычесленных параметров subnets
        """
        interface = IPv4Interface(ipaddr + '/' + str(prefix))
        network = IPv4Network(interface.network)
        subnets = network.subnets(new_prefix=int(calc_prefix))
        subnets_data = []
        for net in subnets:
            sub_interface = IPv4Interface(net.compressed)
            ip_network = net.compressed
            if int(calc_prefix) < 31:
                first_ip = \
                    (sub_interface.network.network_address + 1).compressed
                last_ip = \
                    (sub_interface.network.broadcast_address - 1).compressed
                len_network = \
                    int(sub_interface.network.broadcast_address) - \
                    int(sub_interface.network.network_address) - 1
            elif int(calc_prefix) == 31:
                first_ip = net.network_address.compressed
                last_ip = net.broadcast_address.compressed
                len_network = str(2)
            else:
                first_ip = net.network_address.compressed
                last_ip = net.network_address.compressed
                len_network = str(1)
            subnets_data.append((ip_network, first_ip, last_ip, len_network))
        return subnets_data


if __name__ == '__main__':
    ipcalc = LanCalc()
    ipcalc.mainloop()
